package com.gxar.quick.ar.server.base.model.protocol.editor.web;

import com.gxar.common.entity.SystemCodeEnum;
import com.gxar.common.utils.AssertUtils;
import com.gxar.common.utils.StringUtils;
import com.gxar.quick.ar.server.base.common.constant.ArProtocolConstant;
import com.gxar.quick.ar.server.base.common.enums.protocol.*;
import com.gxar.quick.ar.server.base.model.protocol.*;
import com.gxar.quick.ar.server.base.model.protocol.common.CommonArContentModel;
import com.gxar.quick.ar.server.base.model.protocol.common.CommonArImageIdentifyModel;
import com.gxar.quick.ar.server.base.model.protocol.common.CommonArItemBorderModel;
import com.gxar.quick.ar.server.base.model.protocol.common.CommonArProtocol;
import com.gxar.quick.ar.server.base.model.protocol.editor.EditorArProtocol;
import lombok.Data;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Web编辑器协议
 *
 * @author zxh
 * @version 1.0
 * @created 2024/01/02.
 */
@Data
public class WebEditorArProtocol implements EditorArProtocol {

    /**
     * 编辑器放大系数
     * 编辑器中二维图片等因展示时太小, 都被手动放大了5倍, 而模型没有放大; 展示时模型需缩小5倍
     */
    private static final Integer THREE_JS_EDITOR_PRESENT_SCALE = 5;

    /**
     * 编辑器设定, 识别图默认大小, 3米
     */
    private static final Double IDENTIFY_IMAGE_DEFAULT_SIZE = 3 * ArProtocolConstant.PT_STABLE_VALUE;

    /**
     * 作品编号
     */
    private String workId;

    /**
     * 制作方法
     */
    private ArProductMethodEnum productMethod;

    /**
     * 加载场景
     */
    private ArLoadSceneEnum scene;

    /**
     * 识别模型
     */
    private WebEditorArIdentifyModel identifyModel;

    /**
     * 内容列表
     */
    private List<WebEditorArContentModel> contentModelList;

    @Override
    public ArProtocolEditorEnum getType() {
        return ArProtocolEditorEnum.WEB_EDITOR;
    }

    @Override
    public void fill(CommonInputWorkInfo workInfo, ArLoadSceneEnum arLoadSceneEnum) {
        AssertUtils.notNull(workInfo, SystemCodeEnum.SERVER_ERROR.getRet(), "作品信息不能为空");
        AssertUtils.notNull(arLoadSceneEnum, SystemCodeEnum.SERVER_ERROR.getRet(), "加载场景不能为空");

        this.workId = workInfo.getWorksId();
        this.scene = arLoadSceneEnum;
        this.identifyModel = WebEditorArIdentifyModel.from(workInfo.getIdentify());
        this.contentModelList = WebEditorArContentModel.from(workInfo.getContent());
        this.productMethod = ArProductMethodEnum.getByCode(workInfo.getProductionMethod());
    }

    @Override
    public CommonArProtocol convert2CommonProtocol() {
        AssertUtils.notNull(workId, SystemCodeEnum.SERVER_ERROR.getRet(), "workId不能为空");
        AssertUtils.notNull(productMethod, SystemCodeEnum.SERVER_ERROR.getRet(), "productMethod不能为空");
        AssertUtils.notNull(scene, SystemCodeEnum.SERVER_ERROR.getRet(), "loadScene不能为空");
        AssertUtils.notNull(identifyModel, SystemCodeEnum.SERVER_ERROR.getRet(), "identifyModel不能为空");
        AssertUtils.notNull(contentModelList, SystemCodeEnum.SERVER_ERROR.getRet(), "contentModelList不能为空");

        CommonArProtocol commonArProtocol = new CommonArProtocol();

        CommonArImageIdentifyModel commonArImageIdentifyModel = convert2CommonIdentify(identifyModel);
        commonArProtocol.setIdentifyModel(commonArImageIdentifyModel);
        commonArProtocol.setVersion(identifyModel.getVersion());
        commonArProtocol.setWorkId(workId);
        commonArProtocol.setProductMethod(productMethod);
        commonArProtocol.setEditor(getType());

        // 加载类型不同计算机制不同
        if (ArLoadSceneEnum.PREVIEW.equals(scene) || ArLoadSceneEnum.PLANE.equals(scene)) {
            commonArProtocol.setDisplayMode(ArDisplayModeEnum.PLANE);
            commonArProtocol.setSpaceBaseSize(ArProtocolConstant.PT_STABLE_VALUE);
        } else {
            commonArProtocol.setDisplayMode(ArDisplayModeEnum.MARKER); // 默认
            commonArProtocol.setSpaceBaseSize(commonArImageIdentifyModel.getWorldSize().getWidth());
        }

        List<CommonArContentModel> commonArContentModels = convert2CommonContent(contentModelList);
        commonArProtocol.setContentModelList(commonArContentModels);

        return commonArProtocol;
    }

    /**
     * Web编辑器配置转化
     *
     * @param inputModel web编辑器识别模型
     * @return 通用识别模型
     */
    private CommonArImageIdentifyModel convert2CommonIdentify(WebEditorArIdentifyModel inputModel) {
        AssertUtils.notNull(inputModel, SystemCodeEnum.PARAM_ERROR.getRet(), "输入模型不能为空");

        CommonArImageIdentifyModel outputModel = new CommonArImageIdentifyModel();
        outputModel.setLinkUrl(inputModel.getLinkUrl());

        String scale = StringUtils.isNotEmpty(inputModel.getScale()) ? inputModel.getScale() : CommonArProtocol.DEFAULT_COMMON_IDENTIFY_IMAGE_SCALE;
        outputModel.setScale(ArVector3.from(scale));

        String position = StringUtils.isNotEmpty(inputModel.getPosition()) ? inputModel.getPosition() : CommonArProtocol.DEFAULT_COMMON_IDENTIFY_IMAGE_POSITION;
        outputModel.setPosition(ArVector3.from(position));

        String rotation = StringUtils.isNotEmpty(inputModel.getRotation()) ? inputModel.getRotation() : CommonArProtocol.DEFAULT_COMMON_IDENTIFY_IMAGE_ROTATION;
        outputModel.setRotation(ArVector3.from(rotation));

        if (Objects.nonNull(inputModel.getOriginData())) {
            outputModel.setWorldSize(new ArSizeModel(inputModel.getWidth(), inputModel.getHeight(), inputModel.getDepth()));
            outputModel.setOriginSize(ArSizeModel.create(inputModel.getOriginData()));
        } else {
            // 兼容配置, 编辑器识别图长宽默认固定3m
            outputModel.setWorldSize(new ArSizeModel(IDENTIFY_IMAGE_DEFAULT_SIZE, IDENTIFY_IMAGE_DEFAULT_SIZE, 0d));
            outputModel.setOriginSize(new ArSizeModel(inputModel.getWidth(), inputModel.getHeight(), 0d));
        }

//        outputModel.setQuaternion(); // 不需要
        outputModel.setShowIdentify(inputModel.getShowIdentify());

        return outputModel;
    }

    private List<CommonArContentModel> convert2CommonContent(List<WebEditorArContentModel> inputModels) {
        AssertUtils.notNull(inputModels, SystemCodeEnum.PARAM_ERROR.getRet(), "输入模型不能为空");
        return inputModels.stream().filter(Objects::nonNull).map(this::convert2CommonContent).collect(Collectors.toList());
    }

    /**
     * Web编辑器配置转化
     *
     * @param inputModel web编辑器内容模型
     * @return 通用内容模型
     */
    private CommonArContentModel convert2CommonContent(WebEditorArContentModel inputModel) {
        AssertUtils.notNull(inputModel, SystemCodeEnum.PARAM_ERROR.getRet(), "输入模型不能为空");
        AssertUtils.notNull(inputModel.getScale(), SystemCodeEnum.PARAM_ERROR.getRet(), "scale不能为空");
        AssertUtils.notNull(inputModel.getPosition(), SystemCodeEnum.PARAM_ERROR.getRet(), "position不能为空");
        AssertUtils.notNull(inputModel.getQuaternion(), SystemCodeEnum.PARAM_ERROR.getRet(), "quaternion不能为空");

        CommonArContentModel outputModel = new CommonArContentModel();
        outputModel.setLinkUrl(inputModel.getLinkUrl());
        outputModel.setId(inputModel.getUuid());
        outputModel.setType(inputModel.getType().getName());
        outputModel.setSubType(inputModel.getSubType());
        ArMediaTypeEnum linkType = parseTypeFromLinkUrl(inputModel.getLinkUrl());
        outputModel.setLinkType(linkType);
        outputModel.setIs3dItem(ArMediaTypeEnum.GLTF.equals(linkType));
        if (Objects.nonNull(inputModel.getFillType())) {
            outputModel.setFillType(inputModel.getFillType());
        } else {
            // 默认项: 自动填充
            outputModel.setFillType(ArContentFillTypeEnum.AUTO);
        }
        outputModel.setIsBorder(false);
        outputModel.setAlphaMode("");
        outputModel.setEffect(ArEffectEnum.DEFAULT);

        outputModel.setScale(ArVector3.from(inputModel.getScale()));
        outputModel.setPosition(ArVector3.from(inputModel.getPosition()));
        outputModel.setRotation(ArVector3.from(inputModel.getRotation()));
        outputModel.setQuaternion(new ArQuaternion(inputModel.getQuaternion()));
        outputModel.setOriginQuaternion(new ArQuaternion(inputModel.getOriginQuaternion()));

        if (ArMediaTypeEnum.GLTF.equals(linkType)) {
            // 三维物体
            if (Objects.nonNull(inputModel.getOriginData())) {
                outputModel.setOriginSize(new ArSizeModel(inputModel.getOriginData().getWidth(), inputModel.getOriginData().getHeight(), inputModel.getOriginData().getDepth()));

                Double newWorldSizeX = outputModel.getOriginSize().getWidth() * outputModel.getScale().getX() / THREE_JS_EDITOR_PRESENT_SCALE;
                Double newWorldSizeY = outputModel.getOriginSize().getHeight() * outputModel.getScale().getX() / THREE_JS_EDITOR_PRESENT_SCALE;
                Double newWorldSizeZ = outputModel.getOriginSize().getDepth() * outputModel.getScale().getX() / THREE_JS_EDITOR_PRESENT_SCALE;
                outputModel.setWorldSize(new ArSizeModel(newWorldSizeX, newWorldSizeY, newWorldSizeZ));
            } else {
                // 兜底, 至少不报错
                outputModel.setOriginSize(new ArSizeModel(inputModel.getWidth(), inputModel.getHeight(), inputModel.getDepth()));
                outputModel.setWorldSize(new ArSizeModel(inputModel.getWidth(), inputModel.getHeight(), inputModel.getDepth()));
            }
        } else {
            if (Objects.nonNull(inputModel.getOriginData())) {
                outputModel.setOriginSize(new ArSizeModel(inputModel.getOriginData().getWidth(), inputModel.getOriginData().getHeight()));
            } else {
                // 兜底, 至少不报错
                outputModel.setOriginSize(new ArSizeModel(inputModel.getWidth(), inputModel.getHeight()));
            }

            if (Objects.nonNull(inputModel.getBorder())) {
                // 有边框时, 边框为实际展示长宽
                Double newWorldSizeX = inputModel.getBorder().getOuterWidth() * outputModel.getScale().getX();
                Double newWorldSizeY = inputModel.getBorder().getOuterHeight() * outputModel.getScale().getX();
                Double newWorldSizeZ = 0d;
                outputModel.setWorldSize(new ArSizeModel(newWorldSizeX, newWorldSizeY, newWorldSizeZ));
            } else {
                // 无边框时
                Double newWorldSizeX = outputModel.getOriginSize().getWidth() * outputModel.getScale().getX();
                Double newWorldSizeY = outputModel.getOriginSize().getHeight() * outputModel.getScale().getX();
                Double newWorldSizeZ = outputModel.getOriginSize().getDepth() * outputModel.getScale().getX();
                outputModel.setWorldSize(new ArSizeModel(newWorldSizeX, newWorldSizeY, newWorldSizeZ));
            }
        }

        if (Objects.nonNull(inputModel.getText())) {
            outputModel.setText(inputModel.getText().toCommonModel());
        }

        if (Objects.nonNull(inputModel.getAnimation())) {
            outputModel.setAnimation(inputModel.getAnimation().toCommonModel());
        }

        if (Objects.nonNull(inputModel.getBorder())) {
            CommonArItemBorderModel borderModel = inputModel.getBorder().toCommonModel();

            borderModel.setId(outputModel.getId() + "-border");
            borderModel.setSubType(ArContentSubTypeEnum.DEFAULT);
            borderModel.setPosition(outputModel.getPosition());
            borderModel.setRotation(outputModel.getRotation());
            borderModel.setScale(outputModel.getScale());
            borderModel.setIsBorder(true);
            borderModel.setIs3dItem(false);
            borderModel.setAlphaMode(ArAlphaMode.BLEND.getName());
            borderModel.setEffect(ArEffectEnum.DEFAULT);
            borderModel.setFillType(outputModel.getFillType()); // 沿用

            outputModel.setBorder(borderModel);
            outputModel.setHasBorder(true);
        } else {
            outputModel.setHasBorder(false);
        }

        return outputModel;
    }

    // TODO: 兜底使用, 后期移除
    private ArMediaTypeEnum parseTypeFromLinkUrl(String linkUrl) {
        if (StringUtils.isEmpty(linkUrl)) {
            return null;
        }

        String urlLower = linkUrl.toLowerCase();
        if (urlLower.endsWith("gltf") || urlLower.endsWith("glb")) {
            return ArMediaTypeEnum.GLTF;
        } else if (urlLower.endsWith("mp4")) {
            return ArMediaTypeEnum.VIDEO;
        } else if (urlLower.endsWith("mp3")) {
            return ArMediaTypeEnum.MUSIC;
        } else {
            return ArMediaTypeEnum.IMAGE;
        }
    }
}
